//
//  USlideNavigationController.m
//  YiShangPin
//
//  Created by 衣尚品1 on 15/3/5.
//  Copyright (c) 2015年 yishangpin2. All rights reserved.
//

#import "USlideNavigationController.h"

@interface USlideNavigationController()
@property (nonatomic, strong) UITapGestureRecognizer *tapRecognizer;
@property (nonatomic, strong) UIPanGestureRecognizer *panRecognizer;
@property (nonatomic, assign) CGPoint draggingPoint;
@end

@implementation USlideNavigationController
@synthesize righMenu;
@synthesize leftMenu;
@synthesize tapRecognizer;
@synthesize panRecognizer;
@synthesize draggingPoint;
@synthesize leftbarButtonItem;
@synthesize rightBarButtonItem;
@synthesize enableSwipeGesture;

#define MENU_OFFSET 240
#define MENU_SLIDE_ANIMATION_DURATION .3
#define MENU_QUICK_SLIDE_ANIMATION_DURATION .1
#define MENU_IMAGE @"menu-button"

static USlideNavigationController *singletonInstance;

#pragma mark - Initialization -

+ (USlideNavigationController *)sharedInstance
{
    return singletonInstance;
}

- (void)awakeFromNib
{
    [self setup];
}

- (id)initWithRootViewController:(UIViewController *)rootViewController
{
    if (self = [super initWithRootViewController:rootViewController])
    {
        [self setup];
    }
    
    return self;
}

- (id)init
{
    if (self = [super init])
    {
        [self setup];
    }
    
    return self;
}

- (void)setup
{
    self.avoidSwitchingToSameClassViewController = YES;
    singletonInstance = self;
    self.delegate = self;
    
    self.view.layer.shadowColor = [UIColor darkGrayColor].CGColor;
    self.view.layer.shadowRadius = 10;
    self.view.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.view.bounds].CGPath;
    self.view.layer.shadowOpacity = 1;
    self.view.layer.shouldRasterize = YES;
    self.view.layer.rasterizationScale = [UIScreen mainScreen].scale;
    
    [self setEnableSwipeGesture:YES];
}

#pragma mark - Public Methods -

- (void)switchToViewController:(UIViewController *)viewController withCompletion:(void (^)())completion
{
    if (self.avoidSwitchingToSameClassViewController && [self.topViewController isKindOfClass:viewController.class])
    {
        [self closeMenuWithCompletion:completion];
        return;
    }
    
    __block CGRect rect = self.view.frame;
    
    if ([self isMenuOpen])
    {
        [UIView animateWithDuration:MENU_SLIDE_ANIMATION_DURATION
                              delay:0
                            options:UIViewAnimationOptionCurveEaseOut
                         animations:^{
                             rect.origin.x = (rect.origin.x > 0) ? rect.size.width : -1*rect.size.width;
                             self.view.frame = rect;
                         } completion:^(BOOL finished) {
                             
                             [super popToRootViewControllerAnimated:NO];
                             [super pushViewController:viewController animated:NO];
                             
                             [self closeMenuWithCompletion:^{
                                 if (completion)
                                     completion();
                             }];
                         }];
    }
    else
    {
        [super popToRootViewControllerAnimated:NO];
        [super pushViewController:viewController animated:YES];
        
        if (completion)
            completion();
    }
}

- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
{
    if ([self isMenuOpen])
    {
        [self closeMenuWithCompletion:^{
            [super popToRootViewControllerAnimated:animated];
        }];
    }
    else
    {
        return [super popToRootViewControllerAnimated:animated];
    }
    
    return nil;
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if ([self isMenuOpen])
    {
        [self closeMenuWithCompletion:^{
            [super pushViewController:viewController animated:animated];
        }];
    }
    else
    {
        [super pushViewController:viewController animated:animated];
    }
}

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if ([self isMenuOpen])
    {
        [self closeMenuWithCompletion:^{
            [super popToViewController:viewController animated:animated];
        }];
    }
    else
    {
        return [super popToViewController:viewController animated:animated];
    }
    
    return nil;
}

#pragma mark - Private Methods -

- (UIBarButtonItem *)barButtonItemForMenu:(Menu5)menu
{
    SEL selector = (menu == MenuLeft5) ? @selector(leftMenuSelected:) : @selector(righttMenuSelected:);
    UIBarButtonItem *customButton = (menu == MenuLeft5) ? self.leftbarButtonItem : self.rightBarButtonItem;
    
    if (customButton)
    {
        customButton.action = selector;
        customButton.target = self;
        return customButton;
    }
    else
    {
        UIImage *image = [UIImage imageNamed:MENU_IMAGE];
        return [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:selector];
    }
}

- (BOOL)isMenuOpen
{
    return (self.view.frame.origin.x == 0) ? NO : YES;
}

- (BOOL)shouldDisplayMenu:(Menu5)menu forViewController:(UIViewController *)vc
{
    if (menu == MenuRight5)
    {
        if ([vc respondsToSelector:@selector(slideNavigationControllerShouldDisplayRightMenu)] &&
            [(UIViewController<USlideNavigationControllerDelegate> *)vc slideNavigationControllerShouldDisplayRightMenu])
        {
            return YES;
        }
    }
    if (menu == MenuLeft5)
    {
        if ([vc respondsToSelector:@selector(slideNavigationControllerShouldDisplayLeftMenu)] &&
            [(UIViewController<USlideNavigationControllerDelegate> *)vc slideNavigationControllerShouldDisplayLeftMenu])
        {
            return YES;
        }
    }
    
    return NO;
}

- (void)openMenu:(Menu5)menu withDuration:(float)duration andCompletion:(void (^)())completion
{
    [self.topViewController.view addGestureRecognizer:self.tapRecognizer];
    
    if (menu == MenuLeft5)
    {
        [self.righMenu.view removeFromSuperview];
        [self.view.window insertSubview:self.leftMenu.view atIndex:0];
    }
    else
    {
        [self.leftMenu.view removeFromSuperview];
        [self.view.window insertSubview:self.righMenu.view atIndex:0];
    }
    
    [UIView animateWithDuration:duration
                          delay:0
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         CGRect rect = self.view.frame;
                         rect.origin.x = (menu == MenuLeft5) ? (rect.size.width - MENU_OFFSET) : ((rect.size.width - MENU_OFFSET )* -1);
                         self.view.frame = rect;
                     }
                     completion:^(BOOL finished) {
                         if (completion)
                             completion();
                     }];
}

- (void)openMenu:(Menu5)menu withCompletion:(void (^)())completion
{
    [self openMenu:menu withDuration:MENU_SLIDE_ANIMATION_DURATION andCompletion:completion];
}

- (void)closeMenuWithDuration:(float)duration andCompletion:(void (^)())completion
{
    [self.topViewController.view removeGestureRecognizer:self.tapRecognizer];
    
    [UIView animateWithDuration:duration
                          delay:0
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         CGRect rect = self.view.frame;
                         rect.origin.x = 0;
                         self.view.frame = rect;
                     }
                     completion:^(BOOL finished) {
                         if (completion)
                             completion();
                     }];
}

- (void)closeMenuWithCompletion:(void (^)())completion
{
    [self closeMenuWithDuration:MENU_SLIDE_ANIMATION_DURATION andCompletion:completion];
}

#pragma mark - UINavigationControllerDelegate Methods -

- (void)navigationController:(UINavigationController *)navigationController
      willShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animated
{
    if ([self shouldDisplayMenu:MenuLeft5 forViewController:viewController])
        viewController.navigationItem.leftBarButtonItem = [self barButtonItemForMenu:MenuLeft5];
    
    if ([self shouldDisplayMenu:MenuRight5 forViewController:viewController])
        viewController.navigationItem.rightBarButtonItem = [self barButtonItemForMenu:MenuRight5];
}

#pragma mark - IBActions -

- (void)leftMenuSelected:(id)sender
{
    if ([self isMenuOpen])
        [self closeMenuWithCompletion:nil];
    else
        [self openMenu:MenuLeft5 withCompletion:nil];
    
}

- (void)righttMenuSelected:(id)sender
{
    if ([self isMenuOpen])
        [self closeMenuWithCompletion:nil];
    else
        [self openMenu:MenuRight5 withCompletion:nil];
}

#pragma mark - Gesture Recognizing -

- (void)tapDetected:(UITapGestureRecognizer *)tapRecognizer
{
    [self closeMenuWithCompletion:nil];
}

- (void)panDetected:(UIPanGestureRecognizer *)aPanRecognizer
{
    static NSInteger velocityForFollowingDirection = 1000;
    
    CGPoint translation = [aPanRecognizer translationInView:aPanRecognizer.view];
    CGPoint velocity = [aPanRecognizer velocityInView:aPanRecognizer.view];
    
    if (aPanRecognizer.state == UIGestureRecognizerStateBegan)
    {
        self.draggingPoint = translation;
    }
    else if (aPanRecognizer.state == UIGestureRecognizerStateChanged)
    {
        NSInteger movement = translation.x - self.draggingPoint.x;
        CGRect rect = self.view.frame;
        rect.origin.x += movement;
        
        if (rect.origin.x >= self.minXForDragging && rect.origin.x <= self.maxXForDragging)
            self.view.frame = rect;
        
        self.draggingPoint = translation;
        
        if (rect.origin.x > 0)
        {
            [self.righMenu.view removeFromSuperview];
            [self.view.window insertSubview:self.leftMenu.view atIndex:0];
        }
        else
        {
            [self.leftMenu.view removeFromSuperview];
            [self.view.window insertSubview:self.righMenu.view atIndex:0];
        }
    }
    else if (aPanRecognizer.state == UIGestureRecognizerStateEnded)
    {
        NSInteger currentX = self.view.frame.origin.x;
        NSInteger currentXOffset = (currentX > 0) ? currentX : currentX * -1;
        NSInteger positiveVelocity = (velocity.x > 0) ? velocity.x : velocity.x * -1;
        
        // If the speed is high enough follow direction
        if (positiveVelocity >= velocityForFollowingDirection)
        {
            // Moving Right
            if (velocity.x > 0)
            {
                if (currentX > 0)
                {
                    [self openMenu:(velocity.x > 0) ? MenuLeft5 : MenuRight5 withCompletion:nil];
                }
                else
                {
                    [self closeMenuWithDuration:MENU_QUICK_SLIDE_ANIMATION_DURATION andCompletion:nil];
                }
            }
            // Moving Left
            else
            {
                if (currentX > 0)
                {
                    [self closeMenuWithCompletion:nil];
                }
                else
                {
                    Menu5 menu = (velocity.x > 0) ? MenuLeft5 : MenuRight5;
                    
                    if ([self shouldDisplayMenu:menu forViewController:self.visibleViewController])
                        [self openMenu:(velocity.x > 0) ? MenuLeft5 : MenuRight5 withDuration:MENU_QUICK_SLIDE_ANIMATION_DURATION andCompletion:nil];
                }
            }
        }
        else
        {
            if (currentXOffset < self.view.frame.size.width/3)
                [self closeMenuWithCompletion:nil];
            else
                [self openMenu:(currentX > 0) ? MenuLeft5 : MenuRight5 withCompletion:nil];
        }
    }
}

- (NSInteger)minXForDragging
{
    if ([self shouldDisplayMenu:MenuRight5 forViewController:self.topViewController])
    {
        return (self.view.frame.size.width - MENU_OFFSET)  * -1;
    }
    
    return 0;
}

- (NSInteger)maxXForDragging
{
    if ([self shouldDisplayMenu:MenuLeft5 forViewController:self.topViewController])
    {
        return self.view.frame.size.width - MENU_OFFSET;
    }
    
    return 0;
}

#pragma mark - Setter & Getter -

- (UITapGestureRecognizer *)tapRecognizer
{
    if (!tapRecognizer)
    {
        tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapDetected:)];
    }
    
    return tapRecognizer;
}

- (UIPanGestureRecognizer *)panRecognizer
{
    if (!panRecognizer)
    {
        panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panDetected:)];
    }
    
    return panRecognizer;
}

- (void)setEnableSwipeGesture:(BOOL)markEnableSwipeGesture
{
    enableSwipeGesture = markEnableSwipeGesture;
    
    if (enableSwipeGesture)
    {
        [self.view addGestureRecognizer:self.panRecognizer];
    }
    else
    {
        [self.view removeGestureRecognizer:self.panRecognizer];
    }
}

@end
